package com.hfzy.ihk.web.freeswitchCall.context;

/**
 * Created by Administrator on 2018/9/11.
 *
 * @author wws
 */

import com.alibaba.fastjson.JSON;
import com.hfzy.ihk.common.eventSocket.constant.EventSubClassName;
import com.hfzy.ihk.common.eventSocket.enums.EventName;
import com.hfzy.ihk.web.freeswitchCall.biz.FreeswitchCallStateBiz;
import com.hfzy.ihk.web.freeswitchCall.biz.FreeswitchRecordFileBiz;

import com.hfzy.ihk.web.freeswitchCall.service.FreeswitchInfoService;
import com.hfzy.ihk.web.freeswitchCall.support.ColudCallUtils;
//import org.freeswitch.esl.client.IEslEventListener;
import org.freeswitch.esl.client.inbound.Client;
import org.freeswitch.esl.client.inbound.IEslEventListener;
import org.freeswitch.esl.client.inbound.InboundConnectionFailure;
import org.freeswitch.esl.client.internal.Context;
import org.freeswitch.esl.client.internal.IModEslApi;
import org.freeswitch.esl.client.transport.event.EslEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

import static com.hfzy.ihk.common.eventSocket.enums.EventName.CUSTOM;
import static com.hfzy.ihk.common.eventSocket.enums.EventName.RECORD_STOP;


@Configuration
public class EventSocketInitBean implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(EventSocketInitBean.class);

    @Autowired(required = false)
    @Lazy
    FreeswitchRecordFileBiz freeswitchRecordFileBiz;

    @Autowired(required = false)
    ColudCallUtils coludCallUtils;
    @Autowired(required = false)
    FreeswitchCallStateBiz freeswitchCallStateBiz;

    @Autowired(required = false)
    FreeswitchInfoService freeswitchInfoService;

    private static ConcurrentHashMap<String,String>  localConnection =new ConcurrentHashMap<String, String>(10000); //用于储存本机通话,防止多个call服务重复发生状态

    @Value("${freeswitch.host}")
    private String host;
    @Value("${freeswitch.port}")
    private int port;
    @Value("${freeswitch.password}")
    private String password;
    private Client client;
    private Map<String, Boolean> isAnswerMap = null;





    @Override
    public void afterPropertiesSet() throws Exception {

        client = initClient();
    }

    public Client initClient() {

        client = new Client();
        try {
           // client.connect(host, port, password, 10);
           client.connect(new InetSocketAddress(host, port), password, 10);

        } catch (InboundConnectionFailure e) {
            logger.error("freeswitchlog : freeswitch连接eventSocket失败", e);
            logger.error("Connect failed", e);
            return null;
        }
        //注册事件处理程序IEslEventListener
        client.addEventListener(new IEslEventListener() {

//            }
//            @Override
//            public void eventReceived(Context var1,EslEvent event) {
            @Override
            public void onEslEvent(Context context, EslEvent event) {
                try {
                    String messageJsonData = JSON.toJSONString(event);

                    //logger.debug("消息队列接受中间状态完整消息：" + messageJsonData);
                    String eventName = event.getEventHeaders().get("Event-Name");
                    //获取所有的头信息
                    Map<String, String> eventHeaders = event.getEventHeaders();
                    //获取sessionId,语音识别的状态是没有Channel-Call-UUID,只有session_id
                    String sessionId = eventHeaders.get("Channel-Call-UUID")==null?eventHeaders.get("session_id"):eventHeaders.get("Channel-Call-UUID");

                    if (sessionId == null) {

                        logger.debug("呼叫中间状态未找到seeionId,msg:{}", messageJsonData);
                    }else {

                        //判断不是本机发出的不处理
                        if (!hasCall(sessionId)){

                           // logger.info("本次通话不是由此服务发起,所以不处理,sessionId:"+sessionId);
                            return;
                        }
                        if (RECORD_STOP == EventName.fromName(eventName)) {

                            //停止录音,进入录音专用handler 这里不用了，将生产录音合并到呼叫记录中去
                          //  freeswitchRecordFileBiz.dealCallState(event);
                        } else if (CUSTOM!=EventName.fromName(eventName)){ //自定义事件

                            //其他一般的通话相关状态,进去通话状态handler
                            freeswitchCallStateBiz.dealCallState(event);

                        }
                    }
                    Long start = System.currentTimeMillis();

                    logger.info("中间状态入队时间：" + (System.currentTimeMillis() - start));

                    String uuid = event.getEventHeaders().get("Unique-ID");
                    String channelUUid = event.getEventHeaders().get("Channel-Call-UUID");

                    switch (EventName.fromName(eventName)) {
                        case CUSTOM:
                            handleCustomSate(event);
                            break;
                        case PRESENCE_IN:
                        case RE_SCHEDULE:
                            break;
                        case HEARTBEAT:
//                        logger.info("心跳：" + event.getEventBodyLines());
                            //logger.info("freeswitchlog: freeswitch发来心跳消息,msg:{}", messageJsonData);
                            logger.info("freeswitchlog: freeswitch发来心跳");
                            break;

                        case CHANNEL_DATA:
                            logger.info("CHANNEL_DATA：信道数据" + event.getEventBodyLines());
                            break;
                        case CHANNEL_GLOBAL:
                            logger.info("CHANNEL_GLOBAL：" + event.getEventBodyLines());
                            break;
                        case CHANNEL_HOLD:
                            logger.info("CHANNEL_HOLD：信道保持，使用uuid_hold或者接收SDP的readonly" + event.getEventBodyLines());
                            break;
                        case CHANNEL_PARK:
                            logger.info("CHANNEL_PARK：一个呼叫被挂起" + event.getEventBodyLines());
                            break;
                        case CHANNEL_UUID:
                            logger.info("CHANNEL_UUID：uuid事件表示唯一的ID通道已经改变。原来的ID将被报告的旧唯一ID。此事件会发生，当您使用参数origination_uuid时发出命令发起/桥。：" + event.getEventBodyLines());
                            break;
                        case CHANNEL_APPLICATION:
                            logger.info("CHANNEL_APPLICATION：信道产生的应用程序就是事件application=event一般用来捕获呼转" + event.getEventBodyLines());
                            break;
                        case CHANNEL_CREATE:
                            logger.info("CHANNEL_CREATE： 创建事件。 uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_PROGRESS:
                            logger.info("CHANNEL_PROGRESS 进度事件，外呼时对方提醒。或者入呼时提醒。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_ORIGINATE:
                            logger.info("CHANNEL_ORIGINATE 信道发起事件，触发完成发起：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_OUTGOING:
                            logger.info("CHANNEL_OUTGOING 创建一个外呼事件,这里可以作为commit_success（TODO）。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_PROGRESS_MEDIA:
                            logger.info("CHANNEL_PROGRESS_MEDIA 媒体进度事件，外呼时对方提醒。或者入呼时提醒。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_ANSWER:
                            logger.info("CHANNEL_ANSWER 呼叫应答事件。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_BRIDGE:
                            logger.info("CHANNEL_BRIDGE：一个呼叫两个端点之间的桥接事件。" + event.getEventBodyLines());
                            break;
                        case CHANNEL_STATE:
                            logger.info("CHANNEL_STATE 呼叫状态事件。当一个信道切换通话状态时发送。此事件并不包含任何附加信息。。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_CALLSTATE:
                            logger.info("CHANNEL_CALLSTATE 信道呼叫状态事件。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_EXECUTE:
                            logger.info("CHANNEL_EXECUTE PBX正在执行呼叫事件。。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_EXECUTE_COMPLETE:
                            logger.info("CHANNEL_EXECUTE_COMPLETE 执行完成。。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_HANGUP:
                            logger.info("CHANNEL_HANGUP 挂断：uuid:{},channelUUid:{}，cause:{}", uuid, channelUUid, event.getEventHeaders().get("Hangup-Cause"));
                            break;
                        case CHANNEL_HANGUP_COMPLETE:
                            logger.info("CHANNEL_HANGUP_COMPLETE ：挂断完成：uuid:{},channelUUid:{}", uuid, channelUUid);
                            /*isAnswerMap.remove(uuid);
//                        client.sendAsyncApiCommand("bgapi detect_speech", "stop");
//
                            SendMsg msg = new SendMsg(uuid);
                            msg.addCallCommand("execute");
                            msg.addExecuteAppName("detect_speech");
                            msg.addExecuteAppArg("stop");
                            client.sendMessage(msg);*/
                            break;
                        case CHANNEL_DESTROY:
                            logger.info("CHANNEL_DESTROY: 销毁事件。：uuid:{},channelUUid:{}", uuid, channelUUid);
                            break;
                        case CHANNEL_UNHOLD:
                            logger.info("CHANNEL_UNHOLD：触发后uuid_hold关闭<uuid>或者接收到INVITE SDP= SendRecv的" + event.getEventBodyLines());
                            break;
                        case CHANNEL_UNPARK:
                            logger.info("CHANNEL_UNPARK：一个呼叫被取消挂起" + event.getEventBodyLines());
                            break;
                        case CHANNEL_UNBRIDGE:
                            logger.info("CHANNEL_UNBRIDGE：停用桥接事件。" + event.getEventBodyLines());
                            break;

                        case RECV_RTCP_MESSAGE:
                            logger.info("RECV_RTCP_MESSAGE: {}", JSON.toJSONString(event));
                            break;

                        case PLAYBACK_START:
                            logger.info("PLAYBACK_START: 放音开始：sessionId:{},name:{}", uuid, "");
                            break;
                        case PLAYBACK_STOP:
                            logger.info("PLAYBACK_STOP: 放音结束：sessionId:{},name:{}", uuid, "");
                            break;
                        case RECORD_START:
                            logger.info("RECORD_START: 录音开始：sessionId:{},name:{}", uuid, "");
                            break;
                        case RECORD_STOP:
                            logger.info("RECORD_STOP: 录音结束：sessionId:{},name:{}", uuid, "");
                            break;
                        case DTMF:
                            logger.info("DTMF : 接收到按键 ：sessionId:{},name:{}", uuid, event.getEventBodyLines());
                            break;
                        case DETECTED_SPEECH:
                            logger.info("DETECTED_SPEECH： 语音识别结果：{}", JSON.toJSONString(event));
                            /*client.sendAsyncApiCommand("bgapi detect_speech", "resume");
                            msg = new SendMsg(uuid);
                            msg.addCallCommand("execute");
                            msg.addExecuteAppName("detect_speech");
                            msg.addExecuteAppArg("resume");
                            client.sendMessage(msg);*/
                            break;
                        default:
                            logger.warn("没有此事件类型:" + eventName);
                    }


                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error("初始化接受呼叫中间状态队列异常");

                }

            }
//
//            @Override
//            public void backgroundJobResultReceived(EslEvent event) {
//                String uuid = event.getEventHeaders().get("Job-UUID");
//                logger.info(uuid + ":Background job result received+:" + event.getEventName() + "/" + event.getEventHeaders());// +"/"+JoinString(event.getEventHeaders())+"/"+JoinString(event.getEventBodyLines()));
//
//            }
        });


        //定义事件日志输出格式,但是java esl 目前只支持plain格式 ，http://wiki.freeswitch.org/wiki/Event_Socket
        //2012-12-25 19:20:30 [ main:426 ] - [ ERROR ] java.lang.IllegalStateException: Only 'plain' event format is supported at present
        //client.setEventSubscriptions("plain", "all");
        client.setEventSubscriptions(IModEslApi.EventFormat.PLAIN, "CHANNEL_DESTROY " +
                "CHANNEL_CREATE CHANNEL_ANSWER CHANNEL_HANGUP " +
                "CHANNEL_HANGUP_COMPLETE HEARTBEAT " +
                "CUSTOM sofia::register sofia::unregister alibaba_asr::onSentenceEnd alibaba_asr::onTranscriptionCompleted dial::gateway_not_registered dial::lineBusy");

        //initGatewayContact();

        System.out.println("freeswitchlog : freeswitch连接成功!!");

        return client;
    }

    /**
     * 处理自定义消息
     * @param event
     */
    public void handleCustomSate(EslEvent event){

        Map<String, String> eventHeaders = event.getEventHeaders();

        String subclass = eventHeaders.get("Event-Subclass");
        // sofia::register sofia::unregister alibaba_asr::onSentenceEnd dial::gateway_not_registered dial::lineBusy"
        switch (subclass){
            case EventSubClassName.ALIBABA_ASR_ON_SENTENCE_END:
                freeswitchCallStateBiz.handleAlibabaAsr(event);
                break;
                //按线路忙处理
            case EventSubClassName.SOFIA_REGISTER:
            case EventSubClassName.SOFIA_UNREGISTER:
                freeswitchCallStateBiz.userInfo(event);
                break;
            case EventSubClassName.DIAL_GATEWAY_NOT_REGISTER:
                logger.error("网关未注册:gatewayUser：{}，sessionId：{}",eventHeaders.get("gatewayUser"),eventHeaders.get("sessionId"));
//                freeswitchCallStateBiz.handleGatewayNotRegistered(event);
                freeswitchCallStateBiz.handleLineBusy(event);
                break;
            case EventSubClassName.DIAL_LINE_BUSY:
                logger.warn("线路忙:sessionId：{}",eventHeaders.get("sessionId"));
                freeswitchCallStateBiz.handleLineBusy(event);
                break;
            case EventSubClassName.ALIBABA_ASR_ON_TRANSCRIPTION_COMPLETED:
                logger.info("-----语音识别结束标志事件------");
                freeswitchCallStateBiz.handleSpeechRecognitionEnd(event);
                break;
            default:
                logger.error("未处理的自定义消息：subclass:{},sessionId:{}",subclass,eventHeaders.get("sessionId"));
        }

    }

    public Client getClient() {
        return client;
    }

    private InputStream getFileInputStream(String fileName) {
        FileInputStream ins = null;
        try {
            File file = new File(fileName);
            if (!file.exists())
                return null;
            ins = new FileInputStream(file);
        } catch (Exception e) {
            System.out.println(e.fillInStackTrace());
        }
        return ins;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    /**
     * 将此call服务的通话添加到call服务的Map中
     */
    public void addCall(String callSessionId){

        localConnection.put(callSessionId,"busy");

    }

    public boolean hasCall(String callSessionId){

        return localConnection.containsKey(callSessionId);
    }

    public static void clearCallMapByKey(String key){
        try {
            if (key!=null&&localConnection.contains(key)) {
                localConnection.remove(key);
            }
        }catch (Exception e){
            logger.error("清除call失败:{}",key,e);
        }
    }

    /**
     * 清除通话
     */
    public void clearCallMap() {

        localConnection.clear();
    }

    public static ConcurrentHashMap<String, String> getLocalConnection() {
        return localConnection;
    }


}